﻿using Serilog;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using WatchDog.ConfigManages;

namespace WatchDog.WinService
{
    public partial class Service1 : ServiceBase
    {
        private readonly System.Timers.Timer _timer;
        private readonly ConfigManage _configManage;
        private CancellationTokenSource _cts;
        private ConcurrentDictionary<string, Task> _cache = new ConcurrentDictionary<string, Task>();
        private ConcurrentDictionary<string, Task> _cache2 = new ConcurrentDictionary<string, Task>();

        public CancellationToken Token { get; private set; }
        private int _err = 0;
    
        private string strAssemblyDirPath = string.Empty;
        public static LoggingLevelSwitch loggingLevelSwitch = new LoggingLevelSwitch();
        public Service1()
        {
            InitializeComponent();

            string strAssemblyFilePath = Assembly.GetExecutingAssembly().Location;
            strAssemblyDirPath = Path.GetDirectoryName(strAssemblyFilePath);

            loggingLevelSwitch.MinimumLevel = LogEventLevel.Debug;

            Log.Logger = new LoggerConfiguration()
           //.MinimumLevel.Debug()
           .MinimumLevel.ControlledBy(loggingLevelSwitch)
           .WriteTo.File(strAssemblyDirPath + "/Logs/server-.log",
           rollingInterval: RollingInterval.Day,
           retainedFileTimeLimit: TimeSpan.FromDays(7),
           outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
           fileSizeLimitBytes: 5242880)
           //.Filter.ByExcluding(e => (int)e.Level < _minimumLogLevel)
           .CreateLogger();

            _configManage = new ConfigManage();

            _timer = new System.Timers.Timer(1000 * 30) { AutoReset = true };
            _timer.Elapsed += (sender, eventArgs) =>
            {
                Log.Logger.Information($"定时器执行");
                if (_err > 5)
                {
                    Log.Logger.Debug($"定时器任务中发生错误次数已超过5次，已跳过");
                    return;
                }

                try
                {
                    var items = _configManage.GetFileInfo();
                    if (items.Any())
                    {
                        try
                        {
                            foreach (var item in items)
                            {
                                if (item.AutoStart == true)
                                {
                                    if (ProcessHelper.IsStarting(item.FilePath))
                                    {
                                        Log.Logger.Debug($"程序{item.FileName},{item.FilePath}正在运行中...");
                                    }
                                    else
                                    {
                                        Log.Logger.Debug($"程序{item.FileName},{item.FilePath}未运行");

                                        if (item.DelayStart > 0)
                                        {
                                            Log.Logger.Debug($"延迟启动值为：{item.DelayStart} 秒，已加入计划任务");

                                            AddDelayStartTask(item.DelayStart * 1000, item.FilePath);
                                        }
                                        else
                                        {
                                            Log.Logger.Debug($"正在启动...");

                                            ProcessHelper.Starting(item.FilePath);
                                        }

                                        if (item.ScheduledRestart > 0)
                                        {
                                            Log.Logger.Debug($"定时重启值为：{item.ScheduledRestart * 60} 秒，程序将在指定时间后强制结束");

                                            AddDelayStopTask(item.ScheduledRestart * 60000, item.FilePath);
                                        }
                                    }
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.Logger.Error(ex, "执行定时任务时发生错误");
                        }
                    }
                }
                catch (Exception ex)
                {
                    _err += 1;
                    Log.Logger.Error(ex, $"执行定时任务时出错：当前以错误 {_err} 次");
                }
            };
        }
 
        private void AddDelayStopTask(int a, string name)
        {
            if (!_cache.Any(c => c.Key == name))
            {
                var task = Task.Run(async () =>
                {
                    try
                    {
                        await Task.Delay(a, Token);
                        if (!Token.IsCancellationRequested && ProcessHelper.IsStarting(name))
                        {
                            Log.Logger.Debug($"定时关闭任务：正在完成对程序{name}的关闭（定时{a}毫秒）");
                            ProcessHelper.Stop(name);
                        }
                        else
                        {
                            var s = Token.IsCancellationRequested ? "任务被取消" : "程序未运行";
                            Log.Logger.Debug($"定时关闭任务(取消)：取消对程序{name}的定时关闭任务（定时{a}毫秒）,取消原因：{s}");
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Logger.Error(ex, "执行定时关闭任务时出错");
                    }
                }, Token);
                _cache.AddOrUpdate(name, task, (k, v) => v);
            }
            else
            {
                Log.Logger.Debug($"重复定时关闭任务(跳过):{name}");
            }
        }
        private void AddDelayStartTask(int a, string name)
        {
            if (!_cache2.Any(c => c.Key == name))
            {
                var task = Task.Run(async () =>
                {
                    try
                    {
                        await Task.Delay(a, Token);
                        if (!Token.IsCancellationRequested)
                        {
                            Log.Logger.Debug($"延迟启动任务：正在完成对程序{name}的启动（延迟{a}毫秒）");
                            ProcessHelper.Starting(name);
                        }
                        else
                        {
                            Log.Logger.Debug($"延迟启动任务(取消)：取消对程序{name}的延迟启动任务（延迟{a}毫秒）");
                        }
                    }
                    catch (Exception ex)
                    {
                        Log.Logger.Error(ex, "执行延迟启动任务时出错");
                    }
                }, Token);
                _cache2.AddOrUpdate(name, task, (k, v) => v);
            }
            else
            {
                Log.Logger.Debug($"重复延迟启动任务(跳过):{name}");
            }

        }

        protected override void OnStart(string[] args)
        {
            Log.Logger.Information("正在启动服务...");

            Log.Logger.Debug($"开始 Timer");
            _err = 0;
            _timer.Start();

            Log.Logger.Debug("生成新的 CancellationTokenSource 和 Token");
            _cts = new CancellationTokenSource();   
            Token = _cts.Token;
            Token.Register((code) =>
            {
                Log.Logger.Debug($"Token Exit:{code}");
            }, Token.GetHashCode());
            Log.Logger.Debug($"当前 Token HashCode: {Token.GetHashCode()}");
        }

        protected override void OnStop()
        {
            Log.Logger.Information("正在停止服务...");

            Log.Logger.Debug("取消当前执行的任务： Token.Cancel()");
            _cts.Cancel();

            Log.Logger.Debug($"停止 Timer ");
            _timer.Stop();

            Log.Logger.Debug($"清理任务缓存");
            _cache.Clear();
            _cache2.Clear();
            //_cts.Dispose();
            //_timer.Dispose();
        }

        private void Restart()
        {
            Log.Logger.Debug("取消当前执行的任务： Token.Cancel()");
            _cts.Cancel();

            Log.Logger.Debug("生成新的 CancellationTokenSource 和 Token");
            _cts = new CancellationTokenSource();
            Token = _cts.Token;
            Token.Register((code) =>
            {
                Log.Logger.Debug($"Token Exit:{code}");
            },Token.GetHashCode());
            Log.Logger.Debug($"当前 Token HashCode: {Token.GetHashCode()}");

            Log.Logger.Debug($"停止 Timer ");
            _timer.Stop();

            Log.Logger.Debug($"清理任务缓存");
            _cache.Clear();
            _cache2.Clear();

            Log.Logger.Debug($"清理 Config Manage 缓存");
            _configManage.ClearCache(); // 清除缓存，下次取数据时就会从数据库读取，达到刷新监控项的目的

            Log.Logger.Debug($"重新开始 Timer");
            _err = 0;
            _timer.Start();
        }

        protected override void OnCustomCommand(int command)
        {
            base.OnCustomCommand(command);

            // Depending on the integer passed in, the appropriate method is called.
            switch (command)
            {
                case (int)WindowsServiceCustomCommands.ServiceLogLevelDebug:
                    loggingLevelSwitch.MinimumLevel = LogEventLevel.Debug;
                    Log.Logger.Debug("接收到控制程序端日志变更指令，日志记录等级变更为 诊断模式");
                    break;
                case (int)WindowsServiceCustomCommands.ServiceLogLevelWarning:
                    Log.Logger.Debug("接收到控制程序端日志变更指令，日志记录等级变更为 生产模式");
                    loggingLevelSwitch.MinimumLevel = LogEventLevel.Warning;
                    break;
                case (int)WindowsServiceCustomCommands.WatchItemChanged:
                    Log.Logger.Debug("接收到控制程序端监控配置更改通知，将执行清除缓存和计划任务...");
                    Restart();
                    break;
                default:
                    break;
            }
        }
    }
}
